import * as sinon from 'sinon'
import * as step from '@flow-step/step-toolkit'
import { expect } from 'chai'
import proxyquire from 'proxyquire'

describe('setup', () => {
    let sandbox: sinon.SinonSandbox
    let mkdirSyncStub: sinon.SinonStub
    let writeFileSyncStub: sinon.SinonStub
    let exportEnvVarStub: sinon.SinonStub
    let getNugetPrivateRepoCertificateStub: sinon.SinonStub

    let setup: any

    beforeEach(() => {
        sandbox = sinon.createSandbox()

        // 创建 stub
        mkdirSyncStub = sandbox.stub()
        writeFileSyncStub = sandbox.stub()
        exportEnvVarStub = sandbox.stub(step, 'exportEnvVar')
        getNugetPrivateRepoCertificateStub = sandbox.stub(step.certificate, 'getNugetPrivateRepoCertificate')

        // 使用 proxyquire 加载 setup 模块，并替换 fs 和 path 方法
        setup = proxyquire('../src/setup', {
            fs: {
                mkdirSync: mkdirSyncStub,
                writeFileSync: writeFileSyncStub,
            },
            path: {
                dirname: sandbox.stub().returns('/path/to'),
                resolve: sandbox.stub().returns('/resolved/path/to/config'),
            },
        }).setup
    })

    afterEach(() => {
        sandbox.restore()
    })

    it('should create directory and export environment variable', async () => {
        const mockFlowJobToken = 'test-token'
        const serviceConnectionId = 1111
        const mockConfigPath = '/path/to/config'
        const mockRepoIds = ['repo1', 'repo2']

        const mockCertificate = {
            nugetRepoConfigContent: '<config>mock content</config>',
        }

        getNugetPrivateRepoCertificateStub.resolves(mockCertificate)

        await setup(mockFlowJobToken, serviceConnectionId, mockConfigPath, mockRepoIds)

        sinon.assert.calledOnce(mkdirSyncStub)
        sinon.assert.calledWithExactly(mkdirSyncStub, '/path/to', { recursive: true })

        sinon.assert.calledOnce(exportEnvVarStub)
        sinon.assert.calledWithExactly(exportEnvVarStub, 'NUGET_PRIVATE_REPO_CONFIG_PATH', mockConfigPath)
    })

    it('should write certificate content to file', async () => {
        const mockFlowJobToken = 'test-token'
        const serviceConnectionId = 1111
        const mockConfigPath = '/path/to/config'
        const mockRepoIds = ['repo1', 'repo2']

        const mockCertificate = {
            nugetRepoConfigContent: '<config>mock content</config>',
        }

        getNugetPrivateRepoCertificateStub.resolves(mockCertificate)

        await setup(mockFlowJobToken, serviceConnectionId, mockConfigPath, mockRepoIds)

        sinon.assert.calledOnce(writeFileSyncStub)
        sinon.assert.calledWithExactly(writeFileSyncStub, mockConfigPath, mockCertificate.nugetRepoConfigContent)
    })

    it('should throw an error if certificate content is empty', async () => {
        const mockFlowJobToken = 'test-token'
        const serviceConnectionId = 1111
        const mockConfigPath = '/path/to/config'
        const mockRepoIds = ['repo1', 'repo2']

        getNugetPrivateRepoCertificateStub.resolves(undefined)

        try {
            await setup(mockFlowJobToken, serviceConnectionId, mockConfigPath, mockRepoIds)
            expect.fail('Expected setup to throw an error but it did not')
        } catch (error) {
            if (error instanceof Error) {
                expect(error.message).to.equal('NUGET private repo config content is empty')
            } else {
                expect.fail('Caught error is not an instance of Error')
            }
        }
    })

    it('should handle errors when creating directory fails', async () => {
        const mockFlowJobToken = 'test-token'
        const serviceConnectionId = 1111
        const mockConfigPath = '/path/to/config'
        const mockRepoIds = ['repo1', 'repo2']

        mkdirSyncStub.throws(new Error('Failed to create directory'))

        try {
            await setup(mockFlowJobToken, serviceConnectionId, mockConfigPath, mockRepoIds)
            expect.fail('Expected setup to throw an error but it did not')
        } catch (error) {
            if (error instanceof Error) {
                expect(error.message).to.equal('Failed to create directory')
            } else {
                expect.fail('Caught error is not an instance of Error')
            }
        }
    })

    it('should handle errors when writing file fails', async () => {
        const mockFlowJobToken = 'test-token'
        const serviceConnectionId = 1111
        const mockConfigPath = '/path/to/config'
        const mockRepoIds = ['repo1', 'repo2']

        const mockCertificate = {
            nugetRepoConfigContent: '<config>mock content</config>',
        }

        getNugetPrivateRepoCertificateStub.resolves(mockCertificate)

        writeFileSyncStub.throws(new Error('Failed to write file'))

        try {
            await setup(mockFlowJobToken, serviceConnectionId, mockConfigPath, mockRepoIds)
            expect.fail('Expected setup to throw an error but it did not')
        } catch (error) {
            if (error instanceof Error) {
                expect(error.message).to.equal('Failed to write file')
            } else {
                expect.fail('Caught error is not an instance of Error')
            }
        }
    })
})
